package com.shady.diskmanger.dao;

import com.shady.diskmanger.dao.impl.FilesFunctionDaoImpl;
import com.shady.diskmanger.model.BigFile;
import com.shady.diskmanger.model.SmallFile;
import com.shady.diskmanger.utils.FileMd5Utils;
import java.io.*;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

public class FilesFunctionDao implements FilesFunctionDaoImpl {
    private List<SmallFile> smallFileList = new LinkedList<>();
    private List<String> md5List = new ArrayList<>();
    private static final int BUFFER_SIZE = 1024000;
    @Override
    public int deleteRepeatFiles() {
        int filesCount = 0;
        for (SmallFile smallFile : smallFileList) {
            File file = new File(smallFile.getPath());
            if (file.exists()) {
                file.delete();
                filesCount++;
            } else {
                System.out.println(smallFile.getPath() + "不存在或已被删除！");
            }
        }
        return filesCount;
    }

    @Override
    public int recoverRepeatFiles(String src) {
        int fileCount = 0;
        List<SmallFile> list;
        File file = new File(src + "/copy.dat");
        if (!file.exists() || file.length() < 0) {
            return -1;
        }
        try {
            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file));
            list = (List<SmallFile>) inputStream.readObject();
            inputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        for (SmallFile smallFile : list) {
            SmallFile copyDirectoryFile = new SmallFile(smallFile.getName(), src + '/' + smallFile.getName(), smallFile.getSize(), smallFile.getMd5(), src);
            if (copySignalFile(copyDirectoryFile, smallFile.getParentPath())) fileCount++;
        }
        return fileCount;
    }

    @Override
    public int copyRepeatFiles(String dest) {
        int fileCount = 0;
        for (SmallFile smallFile : smallFileList) {
            if (copySignalFile(smallFile, dest)) fileCount++;
        }
        try {
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(dest + "/copy.dat"));
            outputStream.writeObject(smallFileList);
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return fileCount;
    }

    @Override
    public boolean copySignalFile(SmallFile smallFile, String dest) {
        File inputFile = new File(smallFile.getPath());
        File path = new File(dest);
        if (!inputFile.exists() || !path.isDirectory()) {
            return false;
        }
        try {
            BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(inputFile));
            BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(dest + '/' + smallFile.getName()));
            byte[] buffer = new byte[4096];
            int readLine = 0;
            while ((readLine = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, readLine);
            }
            outputStream.flush();
            outputStream.close();
            inputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return true;
    }

    @Override
    public boolean outPutRepeatFilesMessages(String dest) {
        File file = new File(dest);
        if (!file.exists() || !file.isDirectory()) {
            return false;
        }
        try {
            String tableHead = "文件名\t\t\t大小\t\t\t所在文件夹\n";
            BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(dest + "/messages.txt"));
            bufferedWriter.write(tableHead);
            for (SmallFile smallFile : smallFileList) {
                bufferedWriter.write(smallFile.toString());
            }
            bufferedWriter.flush();
            bufferedWriter.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return true;
    }

    @Override
    public boolean isRepeatFile(String md5) {
        return md5List.contains(md5);
    }

    @Override
    public boolean scanneAllFiles(String src) {
        File file1 = new File(src);
        if (!file1.exists() || !file1.isDirectory()) {
            return false;
        }
        ArrayDeque<DirectoryNode> deque = new ArrayDeque<>();
        deque.add(new DirectoryNode(src, 0));
        while (!deque.isEmpty()) {
            DirectoryNode now = deque.poll();
            File file = new File(now.getPath());
            File[] fileList = file.listFiles();
            for (File i : fileList) {
                if (i.isDirectory()) {
                    deque.add(new DirectoryNode(i.getPath(), now.getRoot() + 1));
                    continue;
                }
                SmallFile smallFile = new SmallFile(i.getName(), i.getPath(), i.length(), FileMd5Utils.md5OfFile(i), i.getParent());
                if (isRepeatFile(smallFile.getMd5())) smallFileList.add(smallFile);
                else md5List.add(smallFile.getMd5());
            }
        }
        return true;
    }

    @Override
    public String[] getFiles(String src) {
        return new File(src).list();
    }

    @Override
    public List<SmallFile> getRepeatFiles() {
        return smallFileList.stream().collect(Collectors.toList());
    }

    public void bigFileCopy(BigFile bigFile) throws IOException {
        RandomAccessFile readFile = new RandomAccessFile(bigFile.getSrc(), "r");
        RandomAccessFile writeFile = new RandomAccessFile(bigFile.getDest(), "rws");
        readFile.seek(bigFile.getStartPoint());
        writeFile.seek(bigFile.getStartPoint());
        byte[] buffer = new byte[BUFFER_SIZE];
        int len = 0;
        long now = bigFile.getStartPoint();
        //要保证文件传输过程中不会出现越界访问的现象
        while ((len = readFile.read(buffer)) != -1 && (now < bigFile.getStartPoint() + bigFile.getOperateLength())) {
            writeFile.write(buffer, 0, len);
            now = readFile.getFilePointer();
        }
        readFile.close();
        writeFile.close();
    }

    private class DirectoryNode {
        private String path;
        private int root;

        public String getPath() {
            return path;
        }

        public void setPath(String path) {
            this.path = path;
        }

        public int getRoot() {
            return root;
        }

        public void setRoot(int root) {
            this.root = root;
        }

        public DirectoryNode(String path, int root) {
            this.path = path;
            this.root = root;
        }
    }
}
